iT邦幫忙

2023 iThome 鐵人賽

DAY 29
0
Modern Web

html/css/js的各種操作系列 第 29

[day29]要結束了 來用html/css/javaScript 做個小遊戲吧

  • 分享至 

  • xImage
  •  

倒數第二天,就來運用html/css/javascript做個小遊戲吧

前言

因為只用一點時間平感覺打,最後會說說做的時候遇到的問題與最後的你可以修繕的方法

本次打算做的是老遊戲agar的概念版,不過只有單純的移動與吃食物這樣,分裂的部分有做但前面沒想那麼多所以後面會有很多衝突(最後會說明),w吐食物的部分有想法但分裂沒用出來就沒用了,倒是把失敗的部分改成加速技能ㄏㄏ

結論:最後沒有用出分裂與w,因為某些原因把技能改成單純加速而已,如果只是單純想看作品的話我直接說,就單純的會跟著鼠鏢跑,能吃食物就這樣很簡陋,本篇主旨在我的看法分享

自我練習與可能會遇到的困難

最後會想做這個,一方面是找不太到東西介紹,另一方面覺得實際去做一個東西,更容易知道會發生甚麼事情,寫程式去刻一個東西絕對是練習的好方法,你可以先把我的程式碼放上去之後,看看版面與動作試著去刻刻看

版面部分挺簡單的要說比較特殊的只有讓食物變大變小的小動畫這應該也不難

做這種說是遊戲還是持續互動介面?來說最麻煩的往往都是javascript的邏輯判定,我就是因為前面邏輯沒想清楚才導致技能沒用好

介面html/css

html

<div class="d-flex justify-content-center flex-wrap">
            <label class="w-100 text-center pt-5">Score:<span id="score">0</span></label>
            <label class="w-100 text-center pt-5">加速(scores>40,冷卻10s)<span class="play" id="speed"></span></label>
            <div class="frame mt-3">
                <span class="play" id="play"></span>
                <span class="food" id="food1"></span>
                <span class="food" id="food2"></span>
                <span class="food" id="food3"></span>
                <span class="food" id="food4"></span>
                <span class="food" id="food5"></span>
            </div>
</div>

css

        <style>
            .frame {
                width: 720px;
                height: 720px;
                border: 2px solid black !important;
                background-color: gray !important;
                position: relative;
            }

            .play {
                width: 20px;
                height: 20px;
                border-radius: 50%;
                background-color: red;
                display: inline-block;
                position: absolute;
                z-index: 5;
            }

            .food {
                width: calc((var(--n) + 5) * 1px);
                height: calc((var(--n) + 5) * 1px);
                border-radius: 50%;
                display: inline-block;
                position: absolute;
                z-index: 2;
                animation: continuous-scale 3s infinite;
            }

            .frame .food:nth-child(2) {
                background-color: aqua;
                --n: 1;
            }

            .frame .food:nth-child(3) {
                background-color: greenyellow;
                --n: 3;
            }

            .frame .food:nth-child(4) {
                background-color: yellow;
                --n: 5;
            }

            .frame .food:nth-child(5) {
                background-color: green;
                --n: 6;
            }

            .frame .food:nth-child(6) {
                background-color: pink;
                --n: 8;
            }

            @keyframes continuous-scale {
                0% {
                    transform: scale(1);
                }

                50% {
                    transform: scale(1.5);
                }

                100% {
                    transform: scale(1);
                }
            }
        </style>

javascript

        <script>
            function getRandomInt(min, max) {
                return Math.floor(Math.random() * (max - min)) + min
            }
            var food_all = []
            var food_xy = []
            var food_score = [2, 4, 5, 6, 8]
            function food_location(food, number) {
                let y = getRandomInt(15, 705)
                let x = getRandomInt(15, 705)
                food.style.top = y + "px"
                food.style.left = x + "px"
                food_xy[number] = [{ x }, { y }]
            }
            for (let i = 1; i <= 5; i++) {
                let foodId = "food" + i
                let foodElement = document.getElementById(foodId)
                food_all.push(foodElement)
                food_location(foodElement, i - 1)
            }
            //被吃後換位子
            function refood(number) {
                food_location(food_all[number], number)
            }
            var player = document.getElementById("play")
            var w = 20 //質量
            var mouseX, mouseY
            //用來取得玩家最終落在的位子
            document.addEventListener("mousemove", mouse_location)
            function mouse_location(event) {
                var bodyX = document.body.offsetWidth //根據畫面大小與遊戲畫面大小來校正
                var bodyY = document.body.offsetHeight
                correctionX = (bodyX - 698) / 2
                correctionY = bodyY - 710
                mouseX = event.clientX - correctionX //最終位子
                mouseY = event.clientY - correctionY
            }
            var x = 0 //初始位子
            var y = 0
            var scores = 0
            var mass = 0
            var power = 5
            var speed_up = 0
            var cool_down = 0
            //用來控制玩家移動速度
            setInterval(function () {
                updatePosition(player)
            }, 33)
            function updatePosition(play) {
                let relativelyX = mouseX - x
                let relativelyY = mouseY - y
                let distance = Math.sqrt(relativelyX * relativelyX + relativelyY * relativelyY) //計算最終位子與玩家的直線距離
                // console.log(relativelyX, relativelyY, distance);
                if (distance > 4) {
                    var stepX = (relativelyX / distance) * power
                    var stepY = (relativelyY / distance) * power
                    //調速
                    power = 5 - Math.sqrt(mass) / 10 + speed_up
                    if (speed_up > 0) {
                        speed_up -= 0.05
                    }
                    if (mass > 40 && cool_down <= 0) {
                        speed.style.backgroundColor = "greenyellow"
                    } else {
                        speed.style.backgroundColor = "red"
                    }
                    if (cool_down > 0) {
                        cool_down -= 0.03
                        console.log(cool_down)
                    }
                    // console.log(stepX, stepY);
                    x += stepX
                    y += stepY
                    if (!(x < -(w / 2) || x > 720 - w / 2)) {
                        play.style.left = x + "px"
                    } else {
                        x -= stepX
                    }
                    if (!(y < -(w / 2) || y > 720 - w / 2)) {
                        play.style.top = y + "px"
                    } else {
                        y -= stepY
                    }
                    //偵測玩家碰到食物
                    for (var i = 0; i <= 4; i++) {
                        if (x + w / 2 > food_xy[i][0].x && x - w / 2 < food_xy[i][0].x && y + w / 2 > food_xy[i][1].y && y - w / 2 < food_xy[i][1].y) {
                            scores += food_score[i]
                            mass += food_score[i]
                            document.getElementById("score").innerHTML = scores
                            refood(i)
                            w = 20 + Math.sqrt(mass) * 1.5
                            play.style.width = w + "px"
                            play.style.height = w + "px"
                        }
                    }
                }
            }
            //space偵測,加速技能
            var speed = document.getElementById("speed")
            document.addEventListener("keydown", function (event) {
                if (event.keyCode === 32 && mass > 40 && cool_down <= 0) {
                    speed_up = 3
                    cool_down = 9.9
                }
            })
 </script>

失敗的分裂程式碼

            var quantity = 1
            document.addEventListener("keydown", function (event) {
                if (event.keyCode === 32 && mass > 40 && quantity == 1) {
                    var playSpan = document.createElement("span")
                    playSpan.className = "play"
                    playSpan.id = "play2"
                    playSpan.style.display = "none"
                    var frameDiv = document.querySelector(".frame.mt-3")
                    frameDiv.appendChild(playSpan)
                    setTimeout(function () {
                        playSpan.style.display = "inline-block"
                        var Split = Splitupdate(playSpan)
                    }, 20)
                }
            })
            function Splitupdate(playSpan) {
                return (
                    setInterval(function () {
                        updatePosition(playSpan, 3)
                    }, 33),
                    33
                )
            }

成功的部分,能夠確實分裂開來變成兩個
失敗的部分,發現變數共用函式套上去直接炸
至於分裂的個體與主體應該要有的物體相隔距離,用出來之後發現前面錯好多就沒再用下去了

如果自我嘗試可能會遇到的困難,與此作品的瑕疵討論

困難1:
碰撞檢查(我這個還是有缺陷),因為會隨著分數不斷變大你吃人的範圍也是要隨著變大
困難2:
top left如果直接用物件.style.top取物件的位子,他會附上???px,px會導致你無法單純的數字比大小,解決方法把它隔離放入xy陣列是其一,但後來我自己覺得會鎖死,改善的方法做一個函數,把生成的部分放入陣列,之後再用splice()刪除,他會把陣列資料刪除並自動往前移

瑕疵1:
我這樣子會寫死,發展性極低所以很不好,改善方式都用js生成,html只要單純的版面即可
瑕疵2:
變數問題,你會發現mass跟score是相同作用的,後面做分裂時發現要這樣用的話,要就把他用陣列儲存,不然就是兩者隔開,質量與score分別計算,不過後續沒有用成功就變這樣了
瑕疵3:
被變數綁死,仔細觀察會發現我整個被變數綁死,已至於分裂後的計算,用js生成食物都會出問題
瑕疵4:
響應式問題,後面發現視窗過小的話那個位子判定又會跑掉

問題1:
為甚麼冷卻的部分不寫==0而是要寫<=0呢?,這就要探討到程式碼中的小數,在表面上是-0.03但在實際上卻可能是-0.030000000000000000000001,所以才必須這樣判定

改善:
應該還有不少問題不過沒察覺,對於要不斷生成新的元素/物件來說,更適合使用javascript來生成這些東西,能讓這個作品更加的真實,要做出完整的版本是可行的,不過物件間的碰撞判定估計會是最難的

最後

當我寫到後面發現時,覺得如果要修改我不如直接重打= =,所以就給他那樣了,順便分享分享,我覺得不少人寫程式也很想知道別人打的時候會遇到甚麼?又怎麼修改?思考的邏輯方向?等等,所以就分享了一下做東西的過程

這個比較是在鍛鍊邏輯的推導,與javascript的靈活運用,如果你是想做出精美的網頁,可以去看看前面的css動畫標籤介紹,再去找個沒有串接api的網頁去訪刻,這也是一種訓練方式

哪天有閒情逸致在試試吧
明天是最後一天就分享一下,對於一個初學者的我是怎麼學習的
然後雖然參賽但只有幾篇是自己滿意的,下一次的鐵人賽分享會做好準備

謝謝觀看~~


上一篇
[day28] 認識js的setInterval()並做個碼表
下一篇
[day30]結語
系列文
html/css/js的各種操作30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言